package com.darker.proxy;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;

/**
 * @auther lj
 * @date 2018/5/3 11:19
 * @email lmwtuedu@163.com
 */
public class Proxy01 {
    public static void main(String[] args) {
        // 创建一个实现Collection接口的动态类
        // 第一个参数为：指定类的加载器，例如创建Collection接口的类加载器
        // 第二个参数为：指定类的接口，例如Collection的接口
        Class<?> classProxy =  Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);

        // 基础类是拿不到getClassLoader,因为Collection的ClassLoader为BootstrapClassLoader
        // 最基础的类，我们是拿不到的，只能为空
        System.out.println("代理产生的字节码的ClassLoader >>>>>>>>> " + classProxy.getClass().getClassLoader());

        // 产生的代理类的名字
        System.out.println("产生代理类的名称：" + classProxy.getName());

        System.out.println("============= 查看动态代理类的构造方法 constructors =============");

        Constructor<?>[] constructors = classProxy.getConstructors();

        for (Constructor<?> constructor : constructors){
            String name = constructor.getName();
            System.out.println("构造方法的名称： " + name);

            // 拼参数列表
            StringBuilder stringBuilder = new StringBuilder(name);
            stringBuilder.append('(');
            Class<?>[] classParams = constructor.getParameterTypes();
            for (Class<?> classParam : classParams){
                System.out.println("构造方法参数的类型： " + classParam.getName());
                stringBuilder.append(classParam.getName()).append(',');
            }

            if (classParams != null && classParams.length != 0){
                // 有参数时，去掉最后一个逗号
                stringBuilder.deleteCharAt(stringBuilder.length() -1);
            }
            stringBuilder.append(')');
            System.out.println(stringBuilder);
        }

        System.out.println("========== 动态代理类的方法以及参数 =============");

        // 利用反射得到所有的方法
        Method[] methods = classProxy.getMethods();
        for (Method method: methods){
            String name = method.getName();
            StringBuilder stringBuilder = new StringBuilder(name);
            stringBuilder.append('(');
            Class<?>[] classParams = method.getParameterTypes();
            for (Class<?> classParam : classParams){
                stringBuilder.append(classParam.getName()).append(',');
            }

            if (classParams != null && classParams.length != 0){
                stringBuilder.deleteCharAt(stringBuilder.length() - 1);
            }
            stringBuilder.append(')');
            System.out.println(stringBuilder);
        }

        // 以上我们已经知道怎么创建类了， 注意是 类，我们创建对象怎么办呢？
        // 查看打印的构造函数，注意入参数是 InvocationHandler
        System.out.println("========== 创建动态类的实例对象及其调用 ============");

        // 因为目前只有一个有参数的构造方法，所以只能利用那个参数产生构造方法
        // classProxy.newInstance() // 错误，只有一个构造方法，没有无参数的构造方法
        // 得到有参数的构造方法
        try {
            Constructor<?> constructor = classProxy.getConstructor(InvocationHandler.class);

            // 利用有参数的构造方法，初始化一个动态的对象，传入InvocationHandler01对象参数
            Collection collection = (Collection) constructor.newInstance(new InvocationHandler01());

            // 打印这个对象为null？
            // null 有两种可能，1种是：对象本身为空，这样会报空指针； 2种是：对象的toString()返回为空
            // 打印为出来null， 因为toString返回为null，因为invoke返回值为空创建成功
            System.out.println(collection);
            System.out.println(collection.getClass().getName());

            // 调用String的 charAt()方法
            Method methodCharAt = String.class.getMethod("charAt", int.class);
            String str = "zhangsan";
            methodCharAt.invoke(str,1);
            System.out.println(methodCharAt.invoke(str,1));
        } catch (Exception e) {
            e.printStackTrace();
        }


    }
}
